home *** CD-ROM | disk | FTP | other *** search
/ Best Tools for JAVA / Best Tools for JAVA.iso / CONVERTR / RTF2HTML / SRC / RTF2HTML.TAR / rtftohtml_src / Libs / lib / reader.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-05  |  48.3 KB  |  2,288 lines

  1. /*
  2.  * - Need to document error code meanings.
  3.  * - Need to do something with \* on destinations.
  4.  * - Make the parameter a long?
  5.  *
  6.  * reader.c - RTF file reader.  Release 1.11.
  7.  *
  8.  * ASCII 10 (\n) and 13 (\r) are ignored and silently discarded.
  9.  * Nulls are also discarded.
  10.  * (although the read hook will still get a look at them.)
  11.  *
  12.  * "\:" is not a ":", it's a control symbol.  But some versions of
  13.  * Word seem to write "\:" for ":".  This reader treats "\:" as a
  14.  * plain text ":"
  15.  *
  16.  * 19 Mar 93
  17.  * - Add hack to skip "{\*\keycode ... }" group in stylesheet.
  18.  * This is probably the wrong thing to do, but it's simple.
  19.  * 13 Jul 93
  20.  * - Add THINK C awareness to malloc() declaration.  Necessary so
  21.  * compiler knows the malloc argument is 4 bytes.  Ugh.
  22.  * 07 Sep 93
  23.  * - Text characters are mapped onto standard codes, which are placed
  24.  * in rtfMinor.
  25.  * - Eliminated use of index() function.
  26.  * 05 Mar 94
  27.  * - Added zillions of new symbols (those defined in RTF spec 1.2).
  28.  * 14 Mar 94
  29.  * - Public functions RTFMsg() and RTFPanic() now take variable arguments.
  30.  * This means RTFPanic() now is used in place of what was formerly the
  31.  * internal function Error().
  32.  * - 8-bit characters are now legal, so they're not converted to \'xx
  33.  * hex char representation now.
  34.  * 01 Apr 94
  35.  * - Added public variables rtfLineNum and rtfLinePos.
  36.  * - #include string.h or strings.h, avoiding strncmp() problem where
  37.  * last argument is treated as zero when prototype isn't available.
  38.  * 04 Apr 94
  39.  * - Treat style numbers 222 and 0 properly as "no style" and "normal".
  40.  * 08 Apr 94
  41.  * - Control symbol table is no longer compiled in.  It's read in the
  42.  * first time that RTFInit() is called from the library file rtf-ctrl.
  43.  * This shrinks all the translator binaries.  rtf-ctrl is built by
  44.  * mkrtf-ctrl.c.
  45.  */
  46.  
  47. # ifndef STRING_H
  48. # define STRING_H <string.h>
  49. # endif
  50.  
  51. # include    <stdio.h>
  52. # include    <ctype.h>
  53. # include    STRING_H
  54. # ifdef STDARG
  55. # include    <stdarg.h>
  56. # else
  57. # ifdef    VARARGS
  58. # include    <varargs.h>
  59. # endif    /* VARARGS */
  60. # endif    /* STDARG */
  61.  
  62. # include    "tokenscan.h"
  63.  
  64. # define    rtfInternal
  65. # include    "rtf.h"
  66. # undef        rtfInternal
  67.  
  68.  
  69. /*
  70.  * Return pointer to new element of type t, or NULL
  71.  * if no memory available.
  72.  */
  73.  
  74. # define    New(t)    ((t *) RTFAlloc (sizeof (t)))
  75.  
  76.  
  77. /* maximum number of character values representable in a byte */
  78.  
  79. # define    charSetSize        256
  80.  
  81. /* charset stack size */
  82.  
  83. # define    maxCSStack        40
  84.  
  85.  
  86. #ifndef THINK_C
  87. extern char    *malloc ();
  88. #else
  89. extern void    *malloc(size_t);
  90. #endif
  91.  
  92. static void    _RTFGetToken ();
  93.  void    _RTFGetToken2 ();
  94. static int    GetChar ();
  95. static void    ReadFontTbl ();
  96. static void    ReadColorTbl ();
  97. static void    ReadStyleSheet ();
  98. static void    ReadInfoGroup ();
  99. static void    ReadPictGroup ();
  100. static void    ReadObjGroup ();
  101. static void    LookupInit ();
  102. static void    Lookup ();
  103. static int    Hash ();
  104.  
  105. static void    CharSetInit ();
  106. static void    ReadCharSetMaps ();
  107.  
  108.  
  109. /*
  110.  * Public variables (listed in rtf.h)
  111.  */
  112.  
  113. int    rtfClass;
  114. int    rtfMajor;
  115. int    rtfMinor;
  116. int    rtfParam;
  117. char    *rtfTextBuf = (char *) NULL;
  118. int    rtfTextLen;
  119.  
  120. long    rtfLineNum;
  121. int    rtfLinePos;
  122.  
  123.  
  124. /*
  125.  * Private stuff
  126.  */
  127.  
  128. static int    pushedChar;    /* pushback char if read too far */
  129.  
  130. static int    pushedClass;    /* pushed token info for RTFUngetToken() */
  131. static int    pushedMajor;
  132. static int    pushedMinor;
  133. static int    pushedParam;
  134. static char    *pushedTextBuf = (char *) NULL;
  135.  
  136. static int    prevChar;
  137. static int    bumpLine;
  138.  
  139.  
  140. static RTFFont    *fontList = (RTFFont *) NULL;    /* these lists MUST be */
  141. static RTFColor    *colorList = (RTFColor *) NULL;    /* initialized to NULL */
  142. static RTFStyle    *styleList = (RTFStyle *) NULL;
  143.  
  144.  
  145. static FILE    *rtffp = stdin;
  146.  
  147. static char    *inputName = (char *) NULL;
  148. static char    *outputName = (char *) NULL;
  149.  
  150.  
  151. /*
  152.  * This array is used to map standard character names onto their numeric codes.
  153.  * The position of the name within the array is the code.
  154.  * stdcharnames.h is generated in the ../h directory.
  155.  */
  156.  
  157. static char    *stdCharName[] =
  158. {
  159. # include    "stdcharnames.h"
  160.     (char *) NULL
  161. };
  162.  
  163.  
  164. /*
  165.  * These arrays are used to map RTF input character values onto the standard
  166.  * character names represented by the values.  Input character values are
  167.  * used as indices into the arrays to produce standard character codes.
  168.  */
  169.  
  170.  
  171. static char    *genCharSetFile = (char *) NULL;
  172. static int    genCharCode[charSetSize];    /* general */
  173. static int    haveGenCharSet = 0;
  174.  
  175. static char    *symCharSetFile = (char *) NULL;
  176. static int    symCharCode[charSetSize];    /* symbol */
  177. static int    haveSymCharSet = 0;
  178.  
  179. static int    curCharSet = rtfCSGeneral;
  180. static int    *curCharCode = genCharCode;
  181.  
  182. /*
  183.  * By default, the reader is configured to handle charset mapping invisibly,
  184.  * including reading the charset files and switching charset maps as necessary
  185.  * for Symbol font.
  186.  */
  187.  
  188. static int    autoCharSetFlags;
  189.  
  190. /*
  191.  * Stack for keeping track of charset map on group begin/end.  This is
  192.  * necessary because group termination reverts the font to the previous
  193.  * value, which may implicitly change it.
  194.  */
  195.  
  196. static int    csStack[maxCSStack];
  197. int    csTop = 0;
  198.  
  199. /*
  200.  * Initialize the reader.  This may be called multiple times,
  201.  * to read multiple files.  The only thing not reset is the input
  202.  * stream; that must be done with RTFSetStream().
  203.  */
  204.  
  205. void
  206. RTFInit ()
  207. {
  208. int    i;
  209. RTFColor    *cp;
  210. RTFFont        *fp;
  211. RTFStyle    *sp;
  212. RTFStyleElt    *eltList, *ep;
  213.  
  214.     rtfClass = -1;
  215.     pushedClass = -1;
  216.     pushedChar = EOF;
  217.  
  218.     rtfLineNum = 0;
  219.     rtfLinePos = 0;
  220.     prevChar = EOF;
  221.     bumpLine = 0;
  222.  
  223.     if (rtfTextBuf == (char *) NULL)    /* initialize text buffers */
  224.     {
  225.         rtfTextBuf = RTFAlloc (rtfBufSiz);
  226.         pushedTextBuf = RTFAlloc (rtfBufSiz);
  227.         if (rtfTextBuf == (char *) NULL
  228.             || pushedTextBuf == (char *) NULL)
  229.             RTFPanic ("Cannot allocate text buffers.");
  230.         rtfTextBuf[0] = pushedTextBuf[0] = '\0';
  231.     }
  232.  
  233.     RTFFree (inputName);
  234.     RTFFree (outputName);
  235.     inputName = outputName = (char *) NULL;
  236.             
  237.     /* initialize control symbol lookup table */
  238.     LookupInit ();
  239.     
  240.     for (i = 0; i < rtfMaxClass; i++)
  241.         RTFSetClassCallback (i, (RTFFuncPtr) NULL);
  242.     for (i = 0; i < rtfMaxDestination; i++)
  243.         RTFSetDestinationCallback (i, (RTFFuncPtr) NULL);
  244.  
  245.     /* install built-in destination readers */
  246.     RTFSetDestinationCallback (rtfFontTbl, ReadFontTbl);
  247.     RTFSetDestinationCallback (rtfColorTbl, ReadColorTbl);
  248.     RTFSetDestinationCallback (rtfStyleSheet, ReadStyleSheet);
  249.     RTFSetDestinationCallback (rtfInfo, ReadInfoGroup);
  250.     RTFSetDestinationCallback (rtfPict, ReadPictGroup);
  251.     RTFSetDestinationCallback (rtfObject, ReadObjGroup);
  252.  
  253.  
  254.     RTFSetReadHook ((RTFFuncPtr) NULL);
  255.  
  256.     /* dump old lists if necessary */
  257.  
  258.     while (fontList != (RTFFont *) NULL)
  259.     {
  260.         fp = fontList->rtfNextFont;
  261.         RTFFree (fontList->rtfFName);
  262.         RTFFree ((char *) fontList);
  263.         fontList = fp;
  264.     }
  265.     while (colorList != (RTFColor *) NULL)
  266.     {
  267.         cp = colorList->rtfNextColor;
  268.         RTFFree ((char *) colorList);
  269.         colorList = cp;
  270.     }
  271.     while (styleList != (RTFStyle *) NULL)
  272.     {
  273.         sp = styleList->rtfNextStyle;
  274.         eltList = styleList->rtfSSEList;
  275.         while (eltList != (RTFStyleElt *) NULL)
  276.         {
  277.             ep = eltList->rtfNextSE;
  278.             RTFFree (eltList->rtfSEText);
  279.             RTFFree ((char *) eltList);
  280.             eltList = ep;
  281.         }
  282.         RTFFree (styleList->rtfSName);
  283.         RTFFree ((char *) styleList);
  284.         styleList = sp;
  285.     }
  286.  
  287.     CharSetInit ();
  288.     csTop = 0;
  289. }
  290.  
  291.  
  292. /*
  293.  * Set the reader's input stream to the given stream.  Can
  294.  * be used to redirect to other than the default (stdin).
  295.  */
  296.  
  297. void
  298. RTFSetStream (stream)
  299. FILE    *stream;
  300. {
  301.     rtffp = stream;
  302. }
  303.  
  304.  
  305. /*
  306.  * Set or get the input or output file name.  These are never guaranteed
  307.  * to be accurate, only insofar as the calling program makes them so.
  308.  */
  309.  
  310. void
  311. RTFSetInputName (name)
  312. char    *name;
  313. {
  314.     if ((inputName = RTFStrSave (name)) == (char *) NULL)
  315.         RTFPanic ("RTFSetInputName: out of memory");
  316. }
  317.  
  318.  
  319. char *
  320. RTFGetInputName ()
  321. {
  322.     return (inputName);
  323. }
  324.  
  325.  
  326. void
  327. RTFSetOutputName (name)
  328. char    *name;
  329. {
  330.     if ((outputName = RTFStrSave (name)) == (char *) NULL)
  331.         RTFPanic ("RTFSetOutputName: out of memory");
  332. }
  333.  
  334.  
  335. char *
  336. RTFGetOutputName ()
  337. {
  338.     return (outputName);
  339. }
  340.  
  341.  
  342.  
  343. /* ---------------------------------------------------------------------- */
  344.  
  345. /*
  346.  * Callback table manipulation routines
  347.  */
  348.  
  349.  
  350. /*
  351.  * Install or return a writer callback for a token class
  352.  */
  353.  
  354.  
  355. static RTFFuncPtr    ccb[rtfMaxClass];        /* class callbacks */
  356.  
  357.  
  358. void
  359. RTFSetClassCallback (class, callback)
  360. int        class;
  361. RTFFuncPtr    callback;
  362. {
  363.     if (class >= 0 && class < rtfMaxClass)
  364.         ccb[class] = callback;
  365. }
  366.  
  367.  
  368. RTFFuncPtr
  369. RTFGetClassCallback (class)
  370. int    class;
  371. {
  372.     if (class >= 0 && class < rtfMaxClass)
  373.         return (ccb[class]);
  374.     return ((RTFFuncPtr) NULL);
  375. }
  376.  
  377.  
  378. /*
  379.  * Install or return a writer callback for a destination type
  380.  */
  381.  
  382. static RTFFuncPtr    dcb[rtfMaxDestination];    /* destination callbacks */
  383.  
  384.  
  385. void
  386. RTFSetDestinationCallback (dest, callback)
  387. int        dest;
  388. RTFFuncPtr    callback;
  389. {
  390.     if (dest >= 0 && dest < rtfMaxDestination)
  391.         dcb[dest] = callback;
  392. }
  393.  
  394.  
  395. RTFFuncPtr
  396. RTFGetDestinationCallback (dest)
  397. int    dest;
  398. {
  399.     if (dest >= 0 && dest < rtfMaxDestination)
  400.         return (dcb[dest]);
  401.     return ((RTFFuncPtr) NULL);
  402. }
  403.  
  404.  
  405. /* ---------------------------------------------------------------------- */
  406.  
  407. /*
  408.  * Token reading routines
  409.  */
  410.  
  411.  
  412. /*
  413.  * Read the input stream, invoking the writer's callbacks
  414.  * where appropriate.
  415.  */
  416.  
  417. void
  418. RTFRead ()
  419. {
  420.     while (RTFGetToken () != rtfEOF)
  421.         RTFRouteToken ();
  422. }
  423.  
  424.  
  425. /*
  426.  * Route a token.  If it's a destination for which a reader is
  427.  * installed, process the destination internally, otherwise
  428.  * pass the token to the writer's class callback.
  429.  */
  430.  
  431. void
  432. RTFRouteToken ()
  433. {
  434. RTFFuncPtr    p;
  435.  
  436.     if (rtfClass < 0 || rtfClass >= rtfMaxClass)    /* watchdog */
  437.     {
  438.         RTFPanic ("Unknown class %d: %s (reader malfunction)",
  439.                             rtfClass, rtfTextBuf);
  440.     }
  441.     if (RTFCheckCM (rtfControl, rtfDestination))
  442.     {
  443.         /* invoke destination-specific callback if there is one */
  444.         if ((p = RTFGetDestinationCallback (rtfMinor))
  445.                             != (RTFFuncPtr) NULL)
  446.         {
  447.             (*p) ();
  448.             return;
  449.         }
  450.     }
  451.     /* invoke class callback if there is one */
  452.     if ((p = RTFGetClassCallback (rtfClass)) != (RTFFuncPtr) NULL)
  453.         (*p) ();
  454. }
  455.  
  456.  
  457. /*
  458.  * Skip to the end of the current group.  When this returns,
  459.  * writers that maintain a state stack may want to call their
  460.  * state unstacker; global vars will still be set to the group's
  461.  * closing brace.
  462.  */
  463.  
  464. void
  465. RTFSkipGroup ()
  466. {
  467. int    level = 1;
  468. int savecsTop;
  469.     if(csTop>0)savecsTop=csTop-1;
  470.     else savecsTop=0;
  471.  
  472.     RTFGetToken ();
  473.     while (1)
  474.     {
  475.         if (rtfClass == rtfEOF)
  476.             break;
  477.         if (rtfClass == rtfGroup)
  478.         {
  479.             if (rtfMajor == rtfBeginGroup)
  480.                 ++level;
  481.             else if (rtfMajor == rtfEndGroup)
  482.             {
  483.                 if (--level < 1)
  484.                     break;    /* end of initial group */
  485.             }
  486.         }
  487.         _RTFGetToken2();
  488.     }
  489.     csTop=savecsTop;
  490. }
  491.  
  492.  
  493. /*
  494.  * Read one token.  Call the read hook if there is one.  The
  495.  * token class is the return value.  Returns rtfEOF when there
  496.  * are no more tokens.
  497.  */
  498.  
  499. int
  500. RTFGetToken ()
  501. {
  502. RTFFuncPtr    p;
  503.  
  504.     for (;;)
  505.     {
  506.         _RTFGetToken ();
  507.         if ((p = RTFGetReadHook ()) != (RTFFuncPtr) NULL)
  508.             (*p) ();    /* give read hook a look at token */
  509.  
  510.         /* Silently discard newlines, carriage returns, nulls.  */
  511.         if (!(rtfClass == rtfText
  512.             && (rtfMajor == '\n' || rtfMajor == '\r'
  513.                         || rtfMajor == '\0')))
  514.             break;
  515.     }
  516.     return (rtfClass);
  517. }
  518.  
  519.  
  520. /*
  521.  * Install or return a token reader hook.
  522.  */
  523.  
  524. static RTFFuncPtr    readHook;
  525.  
  526.  
  527. void
  528. RTFSetReadHook (f)
  529. RTFFuncPtr    f;
  530. {
  531.     readHook = f;
  532. }
  533.  
  534.  
  535. RTFFuncPtr
  536. RTFGetReadHook ()
  537. {
  538.     return (readHook);
  539. }
  540.  
  541.  
  542. void
  543. RTFUngetToken ()
  544. {
  545.     if (pushedClass >= 0)    /* there's already an ungotten token */
  546.         RTFPanic ("cannot unget two tokens");
  547.     if (rtfClass < 0)
  548.         RTFPanic ("no token to unget");
  549.     pushedClass = rtfClass;
  550.     pushedMajor = rtfMajor;
  551.     pushedMinor = rtfMinor;
  552.     pushedParam = rtfParam;
  553.     (void) strcpy (pushedTextBuf, rtfTextBuf);
  554. }
  555.  
  556.  
  557. int
  558. RTFPeekToken ()
  559. {
  560.     _RTFGetToken ();
  561.     RTFUngetToken ();
  562.     return (rtfClass);
  563. }
  564.  
  565.  
  566. static void
  567. _RTFGetToken ()
  568. {
  569. RTFFont    *fp;
  570.  
  571.     /* first check for pushed token from RTFUngetToken() */
  572.  
  573.     if (pushedClass >= 0)
  574.     {
  575.         rtfClass = pushedClass;
  576.         rtfMajor = pushedMajor;
  577.         rtfMinor = pushedMinor;
  578.         rtfParam = pushedParam;
  579.         (void) strcpy (rtfTextBuf, pushedTextBuf);
  580.         rtfTextLen = strlen (rtfTextBuf);
  581.         pushedClass = -1;
  582.         return;
  583.     }
  584.  
  585.     /*
  586.      * Beyond this point, no token is ever seen twice, which is
  587.      * important, e.g., for making sure no "}" pops the font stack twice.
  588.      */
  589.  
  590.     _RTFGetToken2 ();
  591.     if (rtfClass == rtfText)    /* map RTF char to standard code */
  592.         rtfMinor = RTFMapChar (rtfMajor);
  593.  
  594.     /*
  595.      * If auto-charset stuff is activated, see if anything needs doing,
  596.      * like reading the charset maps or switching between them.
  597.      */
  598.  
  599.     if (autoCharSetFlags == 0)
  600.         return;
  601.  
  602.     if ((autoCharSetFlags & rtfReadCharSet)
  603.         && RTFCheckCM (rtfControl, rtfCharSet))
  604.     {
  605.         ReadCharSetMaps ();
  606.     }
  607.     else if ((autoCharSetFlags & rtfSwitchCharSet)
  608.         && RTFCheckCMM (rtfControl, rtfCharAttr, rtfFontNum))
  609.     {
  610.         if ((fp = RTFGetFont (rtfParam)) != (RTFFont *) NULL)
  611.         {
  612.             if (fp->rtfFName!=NULL&&strncmp (fp->rtfFName, "Symbol", 6) == 0)
  613.                 curCharSet = rtfCSSymbol;
  614.             else
  615.                 curCharSet = rtfCSGeneral;
  616.             RTFSetCharSet (curCharSet);
  617.         }
  618.     }
  619.     else if ((autoCharSetFlags & rtfSwitchCharSet) && rtfClass == rtfGroup)
  620.     {
  621.         switch (rtfMajor)
  622.         {
  623.         case rtfBeginGroup:
  624.             if (csTop >= maxCSStack)
  625.                 RTFPanic ("_RTFGetToken: stack overflow");
  626.             csStack[csTop++] = curCharSet;
  627.             break;
  628.         case rtfEndGroup:
  629.             if (csTop <= 0)
  630.                 RTFPanic ("_RTFGetToken: stack underflow");
  631.             curCharSet = csStack[--csTop];
  632.             RTFSetCharSet (curCharSet);
  633.             break;
  634.         }
  635.     }
  636. }
  637.  
  638.  
  639. /* this shouldn't be called anywhere but from _RTFGetToken() */
  640.  
  641. void
  642. _RTFGetToken2 ()
  643. {
  644. int    sign;
  645. int    c;
  646.  
  647.     /* initialize token vars */
  648.  
  649.     rtfClass = rtfUnknown;
  650.     rtfParam = rtfNoParam;
  651.     rtfTextBuf[rtfTextLen = 0] = '\0';
  652.  
  653.     /* get first character, which may be a pushback from previous token */
  654.  
  655.     if (pushedChar != EOF)
  656.     {
  657.         c = pushedChar;
  658.         rtfTextBuf[rtfTextLen++] = c;
  659.         rtfTextBuf[rtfTextLen] = '\0';
  660.         pushedChar = EOF;
  661.     }
  662.     else if ((c = GetChar ()) == EOF)
  663.     {
  664.         rtfClass = rtfEOF;
  665.         return;
  666.     }
  667.  
  668.     if (c == '{')
  669.     {
  670.         rtfClass = rtfGroup;
  671.         rtfMajor = rtfBeginGroup;
  672.         return;
  673.     }
  674.     if (c == '}')
  675.     {
  676.         rtfClass = rtfGroup;
  677.         rtfMajor = rtfEndGroup;
  678.         return;
  679.     }
  680.     if (c != '\\')
  681.     {
  682.         /*
  683.          * Two possibilities here:
  684.          * 1) ASCII 9, effectively like \tab control symbol
  685.          * 2) literal text char
  686.          */
  687.         if (c == '\t')            /* ASCII 9 */
  688.         {
  689.             rtfClass = rtfControl;
  690.             rtfMajor = rtfSpecialChar;
  691.             rtfMinor = rtfTab;
  692.         }
  693.         else
  694.         {
  695.             rtfClass = rtfText;
  696.             rtfMajor = c;
  697.         }
  698.         return;
  699.     }
  700.     if ((c = GetChar ()) == EOF)
  701.     {
  702.         /* early eof, whoops (class is rtfUnknown) */
  703.         return;
  704.     }
  705.     if (!isalpha (c))
  706.     {
  707.         /*
  708.          * Three possibilities here:
  709.          * 1) hex encoded text char, e.g., \'d5, \'d3
  710.          * 2) special escaped text char, e.g., \{, \}
  711.          * 3) control symbol, e.g., \_, \-, \|, \<10>
  712.          */
  713.         if (c == '\'')                /* hex char */
  714.         {
  715.         int    c2;
  716.  
  717.             if ((c = GetChar ()) != EOF && (c2 = GetChar ()) != EOF)
  718.             {
  719.                 /* should do isxdigit check! */
  720.                 rtfClass = rtfText;
  721.                 rtfMajor = RTFCharToHex (c) * 16
  722.                         + RTFCharToHex (c2);
  723.                 return;
  724.             }
  725.             /* early eof, whoops (class is rtfUnknown) */
  726.             return;
  727.         }
  728.  
  729.         /* escaped char */
  730.         /*if (index (":{}\\", c) != (char *) NULL) /* escaped char */
  731.         if (c == ':' || c == '{' || c == '}' || c == '\\')
  732.         {
  733.             rtfClass = rtfText;
  734.             rtfMajor = c;
  735.             return;
  736.         } 
  737.  
  738.         /* control symbol */
  739.         Lookup (rtfTextBuf);    /* sets class, major, minor */
  740.         return;
  741.     }
  742.     /* control word */
  743.     while (isalpha (c))
  744.     {
  745.         if ((c = GetChar ()) == EOF)
  746.             break;
  747.     }
  748.  
  749.     /*
  750.      * At this point, the control word is all collected, so the
  751.      * major/minor numbers are determined before the parameter
  752.      * (if any) is scanned.  There will be one too many characters
  753.      * in the buffer, though, so fix up before and restore after
  754.      * looking up.
  755.      */
  756.  
  757.     if (c != EOF)
  758.         rtfTextBuf[rtfTextLen-1] = '\0';
  759.     Lookup (rtfTextBuf);    /* sets class, major, minor */
  760.     if (c != EOF)
  761.         rtfTextBuf[rtfTextLen-1] = c;
  762.  
  763.     /*
  764.      * Should be looking at first digit of parameter if there
  765.      * is one, unless it's negative.  In that case, next char
  766.      * is '-', so need to gobble next char, and remember sign.
  767.      */
  768.  
  769.     sign = 1;
  770.     if (c == '-')
  771.     {
  772.         sign = -1;
  773.         c = GetChar ();
  774.     }
  775.     if (c != EOF && isdigit (c))
  776.     {
  777.         rtfParam = 0;
  778.         while (isdigit (c))    /* gobble parameter */
  779.         {
  780.             rtfParam = rtfParam * 10 + c - '0';
  781.             if ((c = GetChar ()) == EOF)
  782.                 break;
  783.         }
  784.         rtfParam *= sign;
  785.     }
  786.     /*
  787.      * If control symbol delimiter was a blank, gobble it.
  788.      * Otherwise the character is first char of next token, so
  789.      * push it back for next call.  In either case, delete the
  790.      * delimiter from the token buffer.
  791.      */
  792.     if (c != EOF)
  793.     {
  794.         if (c != ' ')
  795.             pushedChar = c;
  796.         rtfTextBuf[--rtfTextLen] = '\0';
  797.     }
  798. }
  799.  
  800.  
  801. /*
  802.  * Read the next character from the input.  This handles setting the
  803.  * current line and position-within-line variables.  Those variable are
  804.  * set correctly whether lines end with CR, LF, or CRLF (the last being
  805.  * the tricky case).
  806.  *
  807.  * bumpLine indicates whether the line number should be incremented on
  808.  * the *next* input character.
  809.  */
  810.  
  811. static int
  812. GetChar ()
  813. {
  814. int    c;
  815. int    oldBumpLine;
  816.  
  817.     if ((c = getc (rtffp)) != EOF)
  818.     {
  819.         rtfTextBuf[rtfTextLen++] = c;
  820.         rtfTextBuf[rtfTextLen] = '\0';
  821.     }
  822.     if (prevChar == EOF)
  823.         bumpLine = 1;
  824.     oldBumpLine = bumpLine;    /* non-zero if prev char was line ending */
  825.     bumpLine = 0;
  826.     if (c == '\r')
  827.         bumpLine = 1;
  828.     else if (c == '\n')
  829.     {
  830.         bumpLine = 1;
  831.         if (prevChar == '\r')        /* oops, previous \r wasn't */
  832.             oldBumpLine = 0;    /* really a line ending */
  833.     }
  834.     ++rtfLinePos;
  835.     if (oldBumpLine)    /* were we supposed to increment the */
  836.     {            /* line count on this char? */
  837.         ++rtfLineNum;
  838.         rtfLinePos = 1;
  839.     }
  840.     prevChar = c;
  841.     return (c);
  842. }
  843.  
  844.  
  845. /*
  846.  * Synthesize a token by setting the global variables to the
  847.  * values supplied.  Typically this is followed with a call
  848.  * to RTFRouteToken().
  849.  *
  850.  * If a param value other than rtfNoParam is passed, it becomes
  851.  * part of the token text.
  852.  */
  853.  
  854. void
  855. RTFSetToken (class, major, minor, param, text)
  856. int    class, major, minor, param;
  857. char    *text;
  858. {
  859.     rtfClass = class;
  860.     rtfMajor = major;
  861.     rtfMinor = minor;
  862.     rtfParam = param;
  863.     if (param == rtfNoParam)
  864.         (void) strcpy (rtfTextBuf, text);
  865.     else
  866.         sprintf (rtfTextBuf, "%s%d", text, param);
  867.     rtfTextLen = strlen (rtfTextBuf);
  868. }
  869.  
  870.  
  871. /* ---------------------------------------------------------------------- */
  872.  
  873. /*
  874.  * Routines to handle mapping of RTF character sets
  875.  * onto standard characters.
  876.  *
  877.  * RTFStdCharCode(name)    given char name, produce numeric code
  878.  * RTFStdCharName(code)    given char code, return name
  879.  * RTFMapChar(c)    map input (RTF) char code to std code
  880.  * RTFSetCharSet(id)    select given charset map
  881.  * RTFGetCharSet()    get current charset map
  882.  *
  883.  * See ../h/README for more information about charset names and codes.
  884.  */
  885.  
  886.  
  887. /*
  888.  * Initialize charset stuff.
  889.  */
  890.  
  891. static void
  892. CharSetInit ()
  893. {
  894.     autoCharSetFlags = (rtfReadCharSet | rtfSwitchCharSet);
  895.     RTFFree (genCharSetFile);
  896.     genCharSetFile = (char *) NULL;
  897.     haveGenCharSet = 0;
  898.     RTFFree (symCharSetFile);
  899.     symCharSetFile = (char *) NULL;
  900.     haveSymCharSet = 0;
  901.     curCharSet = rtfCSGeneral;
  902.     curCharCode = genCharCode;
  903. }
  904.  
  905.  
  906. /*
  907.  * Specify the name of a file to be read when auto-charset-file reading is
  908.  * done.
  909.  */
  910.  
  911. void
  912. RTFSetCharSetMap (name, csId)
  913. char    *name;
  914. int    csId;
  915. {
  916.     if ((name = RTFStrSave (name)) == (char *) NULL)    /* make copy */
  917.         RTFPanic ("RTFSetCharSetMap: out of memory");
  918.     switch (csId)
  919.     {
  920.     case rtfCSGeneral:
  921.         RTFFree (genCharSetFile);    /* free any previous value */
  922.         genCharSetFile = name;
  923.         break;
  924.     case rtfCSSymbol:
  925.         RTFFree (symCharSetFile);    /* free any previous value */
  926.         symCharSetFile = name;
  927.         break;
  928.     }
  929. }
  930.  
  931.  
  932. /*
  933.  * Do auto-charset-file reading.
  934.  */
  935.  
  936. static void
  937. ReadCharSetMaps ()
  938. {
  939. char    buf[rtfBufSiz];
  940.  
  941.     if (genCharSetFile != (char *) NULL)
  942.         (void) strcpy (buf, genCharSetFile);
  943.     else
  944.         sprintf (buf, "%s-gen", &rtfTextBuf[1]);
  945.     if (RTFReadCharSetMap (buf, rtfCSGeneral) == 0)
  946.         RTFPanic ("ReadCharSetMaps: Cannot read charset map %s", buf);
  947.     if (symCharSetFile != (char *) NULL)
  948.         (void) strcpy (buf, symCharSetFile);
  949.     else
  950.         sprintf (buf, "%s-sym", &rtfTextBuf[1]);
  951.     if (RTFReadCharSetMap (buf, rtfCSSymbol) == 0)
  952.         RTFPanic ("ReadCharSetMaps: Cannot read charset map %s", buf);
  953. }
  954.  
  955.  
  956.  
  957. /*
  958.  * Read in a file describing an RTF character set map.  Lines consist of pairs
  959.  * associating character names with character values.
  960.  *
  961.  * If the filename is an absolute pathname, look in the specified location
  962.  * only.  Otherwise try to find the file in the current directory or library.
  963.  */
  964.  
  965. int
  966. RTFReadCharSetMap (file, csId)
  967. char    *file;
  968. int    csId;
  969. {
  970. FILE    *f;
  971. char    buf[rtfBufSiz];
  972. char    *name, *p;
  973. int    *stdCodeArray;
  974. int    stdCode;
  975. int    radix;
  976. int    value;
  977. int    i;
  978. TSScanner    scanner;
  979. char        *scanEscape;
  980. char    *fn = "RTFReadCharSetMap";
  981.  
  982.     switch (csId)
  983.     {
  984.     default:
  985.         return (0);    /* illegal charset id */
  986.     case rtfCSGeneral:
  987.         stdCodeArray = genCharCode;
  988.         break;
  989.     case rtfCSSymbol:
  990.         stdCodeArray = symCharCode;
  991.         break;
  992.     }
  993.  
  994.     if ((f = RTFOpenLibFile (file, "r")) == (FILE *) NULL)
  995.         return (0);
  996.  
  997.     /* clobber current mapping */
  998.  
  999.     for (i = 0; i < charSetSize; i++)
  1000.     {
  1001.         stdCodeArray[i] = rtfSC_nothing;
  1002.     }
  1003.  
  1004.     /*
  1005.      * Turn off scanner's backslash escape mechanism while reading
  1006.      * charset file.  Restore it later.
  1007.      */
  1008.     TSGetScanner (&scanner);
  1009.     scanEscape = scanner.scanEscape;
  1010.     scanner.scanEscape = "";
  1011.     TSSetScanner (&scanner);
  1012.  
  1013.     /* read file */
  1014.  
  1015.     while (fgets (buf, (int) sizeof (buf), f) != (char *) NULL)
  1016.     {
  1017.         if(buf[0] == '#')    /* skip comment lines */
  1018.             continue;
  1019.         TSScanInit (buf);
  1020.         if ((name = TSScan ()) == (char *) NULL)
  1021.             continue;    /* skip blank lines */
  1022.         if ((stdCode = RTFStdCharCode (name)) < 0)
  1023.         {
  1024.             RTFPanic ("%s: unknown character name: %s", fn, name);
  1025.             continue;
  1026.         }
  1027.         if ((p = TSScan ()) == (char *) NULL)
  1028.         {
  1029.             RTFPanic ("%s: malformed charset map line for character %s",
  1030.                                 fn, name);
  1031.             continue;
  1032.         }
  1033.         if (p[1] == '\0')    /* single char - use ascii value */
  1034.             value = p[0];
  1035.         else
  1036.         {
  1037.             radix = 10;
  1038.             if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
  1039.             {
  1040.                 radix = 16;
  1041.                 p += 2;
  1042.             }
  1043.             value = 0;
  1044.             while (*p != '\0')
  1045.                 value = value * radix + RTFCharToHex(*p++);
  1046.         }
  1047.         if (value >= charSetSize)
  1048.         {
  1049.             RTFMsg ("%s: character value %d for %s too high\n",
  1050.                             fn, value, name);
  1051.             RTFPanic ("maximum value is %d", charSetSize - 1);
  1052.         }
  1053.         stdCodeArray[value] = stdCode;
  1054.     }
  1055.     scanner.scanEscape = scanEscape;
  1056.     TSSetScanner (&scanner);
  1057.     fclose(f);
  1058.  
  1059.     switch (csId)
  1060.     {
  1061.     case rtfCSGeneral:
  1062.         haveGenCharSet = 1;
  1063.         break;
  1064.     case rtfCSSymbol:
  1065.         haveSymCharSet = 1;
  1066.         break;
  1067.     }
  1068.  
  1069.     return (1);
  1070. }
  1071.  
  1072.  
  1073. /*
  1074.  * Given a standard character name (a string), find its code (a number).
  1075.  * Return -1 if name is unknown.
  1076.  */
  1077.  
  1078. int
  1079. RTFStdCharCode (name)
  1080. char    *name;
  1081. {
  1082. int    i;
  1083.  
  1084.     for (i = 0; i < rtfSC_MaxChar; i++)
  1085.     {
  1086.         if (strcmp (name, stdCharName[i]) == 0)
  1087.             return (i);
  1088.     }
  1089.     return (-1);
  1090. }
  1091.  
  1092.  
  1093. /*
  1094.  * Given a standard character code (a number), find its name (a string).
  1095.  * Return NULL if code is unknown.
  1096.  */
  1097.  
  1098. char *
  1099. RTFStdCharName (code)
  1100. int    code;
  1101. {
  1102.     if (code < 0 || code >= rtfSC_MaxChar)
  1103.         return ((char *) NULL);
  1104.     return (stdCharName[code]);
  1105. }
  1106.  
  1107.  
  1108. /*
  1109.  * Given an RTF input character code, find standard character code.
  1110.  * The translator should read the appropriate charset maps when it finds a
  1111.  * charset control.  However, the file might not contain one.  In this
  1112.  * case, no map will be available.  When the first attempt is made to
  1113.  * map a character under these circumstances, RTFMapChar() assumes ANSI
  1114.  * and reads the map as necessary.
  1115.  */
  1116.  
  1117. int
  1118. RTFMapChar (c)
  1119. int    c;
  1120. {
  1121.     switch (curCharSet)
  1122.     {
  1123.     case rtfCSGeneral:
  1124.         if (!haveGenCharSet)
  1125.         {
  1126.             if (RTFReadCharSetMap ("ansi-gen", rtfCSGeneral) == 0)
  1127.                 RTFPanic ("RTFMapChar: cannot read ansi-gen");
  1128.         }
  1129.         break;
  1130.     case rtfCSSymbol:
  1131.         if (!haveSymCharSet)
  1132.         {
  1133.             if (RTFReadCharSetMap ("ansi-sym", rtfCSSymbol) == 0)
  1134.                 RTFPanic ("RTFMapChar: cannot read ansi-sym");
  1135.         }
  1136.         break;
  1137.     }
  1138.     if (c < 0 || c >= charSetSize)
  1139.         return (rtfSC_nothing);
  1140.     return (curCharCode[c]);
  1141. }
  1142.  
  1143.  
  1144. /*
  1145.  * Set the current character set.  If csId is illegal, uses general charset.
  1146.  */
  1147.  
  1148. void
  1149. RTFSetCharSet (csId)
  1150. int    csId;
  1151. {
  1152.     switch (csId)
  1153.     {
  1154.     default:        /* use general if csId unknown */
  1155.     case rtfCSGeneral:
  1156.         curCharCode = genCharCode;
  1157.         curCharSet = csId;
  1158.         break;
  1159.     case rtfCSSymbol:
  1160.         curCharCode = symCharCode;
  1161.         curCharSet = csId;
  1162.         break;
  1163.     }
  1164. }
  1165.  
  1166.  
  1167. int
  1168. RTFGetCharSet ()
  1169. {
  1170.     return (curCharSet);
  1171. }
  1172.  
  1173.  
  1174. /* ---------------------------------------------------------------------- */
  1175.  
  1176. /*
  1177.  * Special destination readers.  They gobble the destination so the
  1178.  * writer doesn't have to deal with them.  That's wrong for any
  1179.  * translator that wants to process any of these itself.  In that
  1180.  * case, these readers should be overridden by installing a different
  1181.  * destination callback.
  1182.  *
  1183.  * NOTE: The last token read by each of these reader will be the
  1184.  * destination's terminating '}', which will then be the current token.
  1185.  * That '}' token is passed to RTFRouteToken() - the writer has already
  1186.  * seen the '{' that began the destination group, and may have pushed a
  1187.  * state; it also needs to know at the end of the group that a state
  1188.  * should be popped.
  1189.  *
  1190.  * It's important that rtf.h and the control token lookup table list
  1191.  * as many symbols as possible, because these destination readers
  1192.  * unfortunately make strict assumptions about the input they expect,
  1193.  * and a token of class rtfUnknown will throw them off easily.
  1194.  */
  1195.  
  1196.  
  1197. /*
  1198.  * Read { \fonttbl ... } destination.  Old font tables don't have
  1199.  * braces around each table entry; try to adjust for that.
  1200.  */
  1201.  
  1202. static void
  1203. ReadFontTbl ()
  1204. {
  1205. RTFFont    *fp;
  1206. char    buf[rtfBufSiz], *bp;
  1207. int    old = -1;
  1208. char    *fn = "ReadFontTbl";
  1209.  
  1210.     for (;;)
  1211.     {
  1212.         (void) RTFGetToken ();
  1213.         if (RTFCheckCM (rtfGroup, rtfEndGroup))
  1214.             break;
  1215.         if (old < 0)        /* first entry - determine tbl type */
  1216.         {
  1217.             if (RTFCheckCMM (rtfControl, rtfCharAttr, rtfFontNum))
  1218.                 old = 1;    /* no brace */
  1219.             else if (RTFCheckCM (rtfGroup, rtfBeginGroup))
  1220.                 old = 0;    /* brace */
  1221.             else            /* can't tell! */
  1222.                 RTFPanic ("%s: Cannot determine format", fn);
  1223.         }
  1224.         if (old == 0)        /* need to find "{" here */
  1225.         {
  1226.             if (!RTFCheckCM (rtfGroup, rtfBeginGroup))
  1227.                 RTFPanic ("%s: missing \"{\"", fn);
  1228.             (void) RTFGetToken ();    /* yes, skip to next token */
  1229.         }
  1230.         if ((fp = New (RTFFont)) == (RTFFont *) NULL)
  1231.             RTFPanic ("%s: cannot allocate font entry", fn);
  1232.  
  1233.         fp->rtfNextFont = fontList;
  1234.         fontList = fp;
  1235.  
  1236.         fp->rtfFName = (char *) NULL;
  1237.         fp->rtfFAltName = (char *) NULL;
  1238.         fp->rtfFNum = -1;
  1239.         fp->rtfFFamily = 0;
  1240.         fp->rtfFCharSet = 0;
  1241.         fp->rtfFPitch = 0;
  1242.         fp->rtfFType = 0;
  1243.         fp->rtfFCodePage = 0;
  1244.  
  1245.         while (rtfClass != rtfEOF && !RTFCheckCM (rtfText, ';'))
  1246.         {
  1247.             if (rtfClass == rtfControl)
  1248.             {
  1249.                 switch (rtfMajor)
  1250.                 {
  1251.                 default:
  1252.                     /* ignore token but announce it */
  1253.                     RTFMsg ("%s: unknown token \"%s\"\n",
  1254.                             fn, rtfTextBuf);
  1255.                 case rtfFontFamily:
  1256.                     fp->rtfFFamily = rtfMinor;
  1257.                     break;
  1258.                 case rtfCharAttr:
  1259.                     switch (rtfMinor)
  1260.                     {
  1261.                     default:
  1262.                         break;    /* ignore unknown? */
  1263.                     case rtfFontNum:
  1264.                         fp->rtfFNum = rtfParam;
  1265.                         break;
  1266.                     }
  1267.                     break;
  1268.                 case rtfFontAttr:
  1269.                     switch (rtfMinor)
  1270.                     {
  1271.                     default:
  1272.                         break;    /* ignore unknown? */
  1273.                     case rtfFontCharSet:
  1274.                         fp->rtfFCharSet = rtfParam;
  1275.                         break;
  1276.                     case rtfFontPitch:
  1277.                         fp->rtfFPitch = rtfParam;
  1278.                         break;
  1279.                     case rtfFontCodePage:
  1280.                         fp->rtfFCodePage = rtfParam;
  1281.                         break;
  1282.                     case rtfFTypeNil:
  1283.                     case rtfFTypeTrueType:
  1284.                         fp->rtfFType = rtfParam;
  1285.                         break;
  1286.                     }
  1287.                     break;
  1288.                 }
  1289.             }
  1290.             else if (RTFCheckCM (rtfGroup, rtfBeginGroup))    /* dest */
  1291.             {
  1292.                 RTFSkipGroup ();    /* ignore for now */
  1293.             }
  1294.             else if (rtfClass == rtfText)    /* font name */
  1295.             {
  1296.                 bp = buf;
  1297.                 while (rtfClass != rtfEOF
  1298.                     && !RTFCheckCM (rtfText, ';'))
  1299.                 {
  1300.                     *bp++ = rtfMajor;
  1301.                     (void) RTFGetToken ();
  1302.                 }
  1303.                 *bp = '\0';
  1304.                 fp->rtfFName = RTFStrSave (buf);
  1305.                 if (fp->rtfFName == (char *) NULL)
  1306.                     RTFPanic ("%s: cannot allocate font name", fn);
  1307.                 /* already have next token; don't read one */
  1308.                 /* at bottom of loop */
  1309.                 continue;
  1310.             }
  1311.             else
  1312.             {
  1313.                 /* ignore token but announce it */
  1314.                 RTFMsg ("%s: unknown token \"%s\"\n",
  1315.                             fn, rtfTextBuf);
  1316.             }
  1317.             (void) RTFGetToken ();
  1318.         }
  1319.         if (old == 0)    /* need to see "}" here */
  1320.         {
  1321.             (void) RTFGetToken ();
  1322.             if (!RTFCheckCM (rtfGroup, rtfEndGroup))
  1323.                 RTFPanic ("%s: missing \"}\"", fn);
  1324.         }
  1325.     }
  1326.     if (fp->rtfFNum == -1)
  1327.         RTFPanic ("%s: missing font number", fn);
  1328. /*
  1329.  * Could check other pieces of structure here, too, I suppose.
  1330.  */
  1331.     RTFRouteToken ();    /* feed "}" back to router */
  1332. }
  1333.  
  1334.  
  1335. /*
  1336.  * The color table entries have color values of -1 if
  1337.  * the default color should be used for the entry (only
  1338.  * a semi-colon is given in the definition, no color values).
  1339.  * There will be a problem if a partial entry (1 or 2 but
  1340.  * not 3 color values) is given.  The possibility is ignored
  1341.  * here.
  1342.  */
  1343.  
  1344. static void
  1345. ReadColorTbl ()
  1346. {
  1347. RTFColor    *cp;
  1348. int        cnum = 0;
  1349. char        *fn = "ReadColorTbl";
  1350.  
  1351.     for (;;)
  1352.     {
  1353.         (void) RTFGetToken ();
  1354.         if (RTFCheckCM (rtfGroup, rtfEndGroup))
  1355.             break;
  1356.         if ((cp = New (RTFColor)) == (RTFColor *) NULL)
  1357.             RTFPanic ("%s: cannot allocate color entry", fn);
  1358.         cp->rtfCNum = cnum++;
  1359.         cp->rtfCRed = cp->rtfCGreen = cp->rtfCBlue = -1;
  1360.         cp->rtfNextColor = colorList;
  1361.         colorList = cp;
  1362.         while (RTFCheckCM (rtfControl, rtfColorName))
  1363.         {
  1364.             switch (rtfMinor)
  1365.             {
  1366.             case rtfRed:    cp->rtfCRed = rtfParam; break;
  1367.             case rtfGreen:    cp->rtfCGreen = rtfParam; break;
  1368.             case rtfBlue:    cp->rtfCBlue = rtfParam; break;
  1369.             }
  1370.             RTFGetToken ();
  1371.         }
  1372.         if (!RTFCheckCM (rtfText, (int) ';'))
  1373.             RTFPanic ("%s: malformed entry", fn);
  1374.     }
  1375.     RTFRouteToken ();    /* feed "}" back to router */
  1376. }
  1377.  
  1378.  
  1379. /*
  1380.  * The "Normal" style definition doesn't contain any style number,
  1381.  * all others do.  Normal style is given style rtfNormalStyleNum.
  1382.  */
  1383.  
  1384. static void
  1385. ReadStyleSheet ()
  1386. {
  1387. RTFStyle    *sp;
  1388. RTFStyleElt    *sep, *sepLast;
  1389. char        buf[rtfBufSiz], *bp;
  1390. char        *fn = "ReadStyleSheet";
  1391.  
  1392.     for (;;)
  1393.     {
  1394.         (void) RTFGetToken ();
  1395.         if (RTFCheckCM (rtfGroup, rtfEndGroup))
  1396.             break;
  1397.         if ((sp = New (RTFStyle)) == (RTFStyle *) NULL)
  1398.             RTFPanic ("%s: cannot allocate stylesheet entry", fn);
  1399.         sp->rtfSName = (char *) NULL;
  1400.         sp->rtfSNum = -1;
  1401.         sp->rtfSType = rtfParStyle;
  1402.         sp->rtfSAdditive = 0;
  1403.         sp->rtfSBasedOn = rtfNoStyleNum;
  1404.         sp->rtfSNextPar = -1;
  1405.         sp->rtfSSEList = sepLast = (RTFStyleElt *) NULL;
  1406.         sp->rtfNextStyle = styleList;
  1407.         sp->rtfExpanding = 0;
  1408.         styleList = sp;
  1409.         if (!RTFCheckCM (rtfGroup, rtfBeginGroup))
  1410.             RTFPanic ("%s: missing \"{\"", fn);
  1411.         for (;;)
  1412.         {
  1413.             (void) RTFGetToken ();
  1414.             if (rtfClass == rtfEOF
  1415.                 || RTFCheckCM (rtfText, ';'))
  1416.                 break;
  1417.             if (rtfClass == rtfControl)
  1418.             {
  1419.                 if (RTFCheckMM (rtfSpecialChar, rtfOptDest))
  1420.                     continue;    /* ignore "\*" */
  1421.                 if (RTFCheckMM (rtfParAttr, rtfStyleNum))
  1422.                 {
  1423.                     sp->rtfSNum = rtfParam;
  1424.                     sp->rtfSType = rtfParStyle;
  1425.                     continue;
  1426.                 }
  1427.                 if (RTFCheckMM (rtfCharAttr, rtfCharStyleNum))
  1428.                 {
  1429.                     sp->rtfSNum = rtfParam;
  1430.                     sp->rtfSType = rtfCharStyle;
  1431.                     continue;
  1432.                 }
  1433.                 if (RTFCheckMM (rtfSectAttr, rtfSectStyleNum))
  1434.                 {
  1435.                     sp->rtfSNum = rtfParam;
  1436.                     sp->rtfSType = rtfSectStyle;
  1437.                     continue;
  1438.                 }
  1439.                 if (RTFCheckMM (rtfStyleAttr, rtfBasedOn))
  1440.                 {
  1441.                     sp->rtfSBasedOn = rtfParam;
  1442.                     continue;
  1443.                 }
  1444.                 if (RTFCheckMM (rtfStyleAttr, rtfAdditive))
  1445.                 {
  1446.                     sp->rtfSAdditive = 1;
  1447.                     continue;
  1448.                 }
  1449.                 if (RTFCheckMM (rtfStyleAttr, rtfNext))
  1450.                 {
  1451.                     sp->rtfSNextPar = rtfParam;
  1452.                     continue;
  1453.                 }
  1454.                 if ((sep = New (RTFStyleElt)) == (RTFStyleElt *) NULL)
  1455.                     RTFPanic ("%s: cannot allocate style element", fn);
  1456.                 sep->rtfSEClass = rtfClass;
  1457.                 sep->rtfSEMajor = rtfMajor;
  1458.                 sep->rtfSEMinor = rtfMinor;
  1459.                 sep->rtfSEParam = rtfParam;
  1460.                 if ((sep->rtfSEText = RTFStrSave (rtfTextBuf))
  1461.                                 == (char *) NULL)
  1462.                     RTFPanic ("%s: cannot allocate style element text", fn);
  1463.                 if (sepLast == (RTFStyleElt *) NULL)
  1464.                     sp->rtfSSEList = sep;    /* first element */
  1465.                 else                /* add to end */
  1466.                     sepLast->rtfNextSE = sep;
  1467.                 sep->rtfNextSE = (RTFStyleElt *) NULL;
  1468.                 sepLast = sep;
  1469.             }
  1470.             else if (RTFCheckCM (rtfGroup, rtfBeginGroup))
  1471.             {
  1472.                 /*
  1473.                  * This passes over "{\*\keycode ... }, among
  1474.                  * other things. A temporary (perhaps) hack.
  1475.                  */
  1476.                 RTFSkipGroup ();
  1477.                 continue;
  1478.             }
  1479.             else if (rtfClass == rtfText)    /* style name */
  1480.             {
  1481.                 bp = buf;
  1482.                 while (rtfClass == rtfText)
  1483.                 {
  1484.                     if (rtfMajor == ';')
  1485.                     {
  1486.                         /* put back for "for" loop */
  1487.                         (void) RTFUngetToken ();
  1488.                         break;
  1489.                     }
  1490.                     *bp++ = rtfMajor;
  1491.                     (void) RTFGetToken ();
  1492.                 }
  1493.                 *bp = '\0';
  1494.                 if ((sp->rtfSName = RTFStrSave (buf)) == (char *) NULL)
  1495.                     RTFPanic ("%s: cannot allocate style name", fn);
  1496.             }
  1497.             else        /* unrecognized */
  1498.             {
  1499.                 /* ignore token but announce it */
  1500.                 RTFMsg ("%s: unknown token \"%s\"\n",
  1501.                             fn, rtfTextBuf);
  1502.             }
  1503.         }
  1504.         (void) RTFGetToken ();
  1505.         if (!RTFCheckCM (rtfGroup, rtfEndGroup))
  1506.             RTFPanic ("%s: missing \"}\"", fn);
  1507.  
  1508.         /*
  1509.          * Check over the style structure.  A name is a must.
  1510.          * If no style number was specified, check whether it's the
  1511.          * Normal style (in which case it's given style number
  1512.          * rtfNormalStyleNum).  Note that some "normal" style names
  1513.          * just begin with "Normal" and can have other stuff following,
  1514.          * e.g., "Normal,Times 10 point".  Ugh.
  1515.          *
  1516.          * Some German RTF writers use "Standard" instead of "Normal".
  1517.          */
  1518.         if (sp->rtfSName == (char *) NULL)
  1519.             RTFPanic ("%s: missing style name", fn);
  1520.         if (sp->rtfSNum < 0)
  1521.         {
  1522.             if (strncmp (buf, "Normal", 6) != 0
  1523.                 && strncmp (buf, "Standard", 8) != 0)
  1524.                 RTFPanic ("%s: missing style number", fn);
  1525.             sp->rtfSNum = rtfNormalStyleNum;
  1526.         }
  1527.         if (sp->rtfSNextPar == -1)    /* if \snext not given, */
  1528.             sp->rtfSNextPar = sp->rtfSNum;    /* next is itself */
  1529.     }
  1530.     RTFRouteToken ();    /* feed "}" back to router */
  1531. }
  1532.  
  1533.  
  1534. static void
  1535. ReadInfoGroup ()
  1536. {
  1537.     RTFSkipGroup ();
  1538.     RTFRouteToken ();    /* feed "}" back to router */
  1539. }
  1540.  
  1541.  
  1542. static void
  1543. ReadPictGroup ()
  1544. {
  1545.     RTFSkipGroup ();
  1546.     RTFRouteToken ();    /* feed "}" back to router */
  1547. }
  1548.  
  1549.  
  1550. static void
  1551. ReadObjGroup ()
  1552. {
  1553.     RTFSkipGroup ();
  1554.     RTFRouteToken ();    /* feed "}" back to router */
  1555. }
  1556.  
  1557.  
  1558. /* ---------------------------------------------------------------------- */
  1559.  
  1560. /*
  1561.  * Routines to return pieces of stylesheet, or font or color tables.
  1562.  * References to style 0 are mapped onto the Normal style.
  1563.  */
  1564.  
  1565.  
  1566. RTFStyle *
  1567. RTFGetStyle (num)
  1568. int    num;
  1569. {
  1570. RTFStyle    *s;
  1571.  
  1572.     if (num == -1)
  1573.         return (styleList);
  1574.     for (s = styleList; s != (RTFStyle *) NULL; s = s->rtfNextStyle)
  1575.     {
  1576.         if (s->rtfSNum == num)
  1577.             break;
  1578.     }
  1579.     return (s);        /* NULL if not found */
  1580. }
  1581.  
  1582.  
  1583. RTFFont *
  1584. RTFGetFont (num)
  1585. int    num;
  1586. {
  1587. RTFFont    *f;
  1588.  
  1589.     if (num == -1)
  1590.         return (fontList);
  1591.     for (f = fontList; f != (RTFFont *) NULL; f = f->rtfNextFont)
  1592.     {
  1593.         if (f->rtfFNum == num)
  1594.             break;
  1595.     }
  1596.     return (f);        /* NULL if not found */
  1597. }
  1598.  
  1599.  
  1600. RTFColor *
  1601. RTFGetColor (num)
  1602. int    num;
  1603. {
  1604. RTFColor    *c;
  1605.  
  1606.     if (num == -1)
  1607.         return (colorList);
  1608.     for (c = colorList; c != (RTFColor *) NULL; c = c->rtfNextColor)
  1609.     {
  1610.         if (c->rtfCNum == num)
  1611.             break;
  1612.     }
  1613.     return (c);        /* NULL if not found */
  1614. }
  1615.  
  1616.  
  1617. /* ---------------------------------------------------------------------- */
  1618.  
  1619.  
  1620. /*
  1621.  * Expand style n, if there is such a style.
  1622.  */
  1623.  
  1624. void
  1625. RTFExpandStyle (n)
  1626. int    n;
  1627. {
  1628. RTFStyle    *s;
  1629. RTFStyleElt    *se;
  1630.  
  1631.     if (n == -1 || (s = RTFGetStyle (n)) == (RTFStyle *) NULL)
  1632.         return;
  1633.     if (s->rtfExpanding != 0)
  1634.         RTFPanic ("Style expansion loop, style %d", n);
  1635.     s->rtfExpanding = 1;    /* set expansion flag for loop detection */
  1636.     /*
  1637.      * Expand "based-on" style (unless it's the same as the current
  1638.      * style -- Normal style usually gives itself as its own based-on
  1639.      * style).  Based-on style expansion is done by synthesizing
  1640.      * the token that the writer needs to see in order to trigger
  1641.      * another style expansion, and feeding to token back through
  1642.      * the router so the writer sees it.
  1643.      */
  1644.     if (n != s->rtfSBasedOn)
  1645.     {
  1646.         RTFSetToken (rtfControl, rtfParAttr, rtfStyleNum,
  1647.                             s->rtfSBasedOn, "\\s");
  1648.         RTFRouteToken ();
  1649.     }
  1650.     /*
  1651.      * Now route the tokens unique to this style.  RTFSetToken()
  1652.      * isn't used because it would add the param value to the end
  1653.      * of the token text, which already has it in.
  1654.      */
  1655.     for (se = s->rtfSSEList; se != (RTFStyleElt *) NULL; se = se->rtfNextSE)
  1656.     {
  1657.         rtfClass = se->rtfSEClass;
  1658.         rtfMajor = se->rtfSEMajor;
  1659.         rtfMinor = se->rtfSEMinor;
  1660.         rtfParam = se->rtfSEParam;
  1661.         (void) strcpy (rtfTextBuf, se->rtfSEText);
  1662.         rtfTextLen = strlen (rtfTextBuf);
  1663.         RTFRouteToken ();
  1664.     }
  1665.     s->rtfExpanding = 0;    /* done - clear expansion flag */
  1666. }
  1667.  
  1668.  
  1669. /* ---------------------------------------------------------------------- */
  1670.  
  1671. /*
  1672.  * Control symbol lookup routines
  1673.  */
  1674.  
  1675.  
  1676. typedef struct RTFCtrl    RTFCtrl;
  1677.  
  1678. struct RTFCtrl
  1679. {
  1680.     int    major;    /* major number */
  1681.     int    minor;    /* minor number */
  1682.     char    *str;    /* symbol name */
  1683.     int    hash;    /* symbol name hash value */
  1684. };
  1685.  
  1686. /*
  1687.  * A minor number of -1 means the token has no minor number
  1688.  * (all valid minor numbers are >= 0).
  1689.  */
  1690.  
  1691. static RTFCtrl    **rtfCtrl = (RTFCtrl **) NULL;
  1692. static int    nKeys;
  1693.  
  1694.  
  1695. /*
  1696.  * Initialize lookup table hash values.  Only need to do this once.
  1697.  */
  1698.  
  1699. # define        ctrlFileName    "rtf-ctrl"
  1700.  
  1701. static void
  1702. LookupInit ()
  1703. {
  1704. FILE    *f;
  1705. RTFCtrl *rp;
  1706. char    buf[rtfBufSiz];
  1707. int     line = 0, i;
  1708. char    *p1, *p2, *p3, c;
  1709. TSScanner       scanner;
  1710. char            *scanEscape;
  1711. char    *fn = "LookupInit";
  1712.  
  1713.         if (rtfCtrl != (RTFCtrl **) NULL)       /* already initialized */
  1714.                 return;
  1715.  
  1716.         if ((f = RTFOpenLibFile (ctrlFileName, "r")) == (FILE *) NULL)
  1717.                 RTFPanic ("%s: cannot open %s file.", fn, ctrlFileName);
  1718.  
  1719.         /*
  1720.          * Turn off scanner's backslash escape mechanism while reading
  1721.          * file.  Restore it later.
  1722.          */
  1723.         TSGetScanner (&scanner);
  1724.         scanEscape = scanner.scanEscape;
  1725.         scanner.scanEscape = "";
  1726.         TSSetScanner (&scanner);
  1727.  
  1728.         while (fgets (buf, (int) sizeof (buf), f) != (char *) NULL)
  1729.         {
  1730.                 if (buf[0] == '#')              /* skip comments */
  1731.                         continue;
  1732.                 ++line;
  1733.                 TSScanInit (buf);
  1734.                 p1 = TSScan ();
  1735.                 p2 = TSScan ();
  1736.                 p3 = TSScan ();
  1737.                 if (line == 1)  /* this is the line with the count on it */
  1738.                 {
  1739.                         if (p1 == (char *) NULL)        /* malformed */
  1740.                                 break;
  1741.                         nKeys = atoi (p1);
  1742.                         rtfCtrl = (RTFCtrl **) RTFAlloc (nKeys * sizeof (RTFCtrl *));
  1743.                         if (rtfCtrl == (RTFCtrl **) NULL)
  1744.                                 RTFPanic ("%s: out of memory.", fn);
  1745.                         for (i = 0; i < nKeys; i++)
  1746.                         {
  1747.                                 rp = (RTFCtrl *) RTFAlloc (sizeof (RTFCtrl));
  1748.                                 if (rp == (RTFCtrl *) NULL)
  1749.                                         RTFPanic ("%s: out of memory.", fn);
  1750.                                 rtfCtrl[i] = rp;
  1751.                         }
  1752.                 }
  1753.                 else
  1754.                 {
  1755.                         if (p3 == (char *) NULL)        /* malformed */
  1756.                                 break;
  1757.                         if (line - 1 > nKeys)           /* malformed */
  1758.                                 break;
  1759.                         rp = rtfCtrl[line-2];
  1760.                         rp->major = atoi (p1);
  1761.                         rp->minor = atoi (p2);
  1762.                         /* reprocess string to remove embedded escapes */
  1763.                         p1 = p2 = p3;
  1764.                         while ((c = *p1++) != '\0')
  1765.                         {
  1766.                                 /*
  1767.                                  * Escaped character.  Default is to use next
  1768.                                  * character unmodified, but \n and \r are
  1769.                                  * turned into linefeed and carriage return.
  1770.                                  */
  1771.                                 if (c == '\\')
  1772.                                 {
  1773.                                         c = *p1++;
  1774.                                         switch (c)
  1775.                                         {
  1776.                                         case 'n':
  1777.                                                 c = '\n';
  1778.                                                 break;
  1779.                                         case 'r':
  1780.                                                 c = '\r';
  1781.                                                 break;
  1782.                                         }
  1783.                                 }
  1784.                                 *p2++ = c;
  1785.                         }
  1786.                         *p2 = '\0';
  1787.                         if ((rp->str = RTFStrSave (p3)) == (char *) NULL)
  1788.                                 RTFPanic ("%s: out of memory.", fn);
  1789.                         rp->hash = Hash (rp->str);
  1790.                 }
  1791.         }
  1792.         scanner.scanEscape = scanEscape;
  1793.         TSSetScanner (&scanner);
  1794.         (void) fclose (f);
  1795.  
  1796.         if (rtfCtrl == (RTFCtrl **) NULL || line - 1 != nKeys)
  1797.                 RTFPanic ("%s: %s file contents malformed.", fn, ctrlFileName);
  1798. }
  1799.  
  1800.  
  1801. /*
  1802.  * Determine major and minor number of control token.  If it's
  1803.  * not found, the class turns into rtfUnknown.
  1804.  */
  1805.  
  1806. static void
  1807. Lookup (s)
  1808. char    *s;
  1809. {
  1810. RTFCtrl    *rp;
  1811. int    hash;
  1812. int    i;
  1813.  
  1814.     ++s;            /* skip over the leading \ character */
  1815.     hash = Hash (s);
  1816.     for (i = 0; i < nKeys; i++)
  1817.     {
  1818.         rp = rtfCtrl[i];
  1819.         if (hash == rp->hash && strcmp (s, rp->str) == 0)
  1820.         {
  1821.             rtfClass = rtfControl;
  1822.             rtfMajor = rp->major;
  1823.             rtfMinor = rp->minor;
  1824.             return;
  1825.         }
  1826.     }
  1827.     rtfClass = rtfUnknown;
  1828. }
  1829.  
  1830.  
  1831. /*
  1832.  * Compute hash value of symbol
  1833.  */
  1834.  
  1835. static int
  1836. Hash (s)
  1837. char    *s;
  1838. {
  1839. char    c;
  1840. int    val = 0;
  1841.  
  1842.     while ((c = *s++) != '\0')
  1843.         val += (int) c;
  1844.     return (val);
  1845. }
  1846.  
  1847.  
  1848. /* ---------------------------------------------------------------------- */
  1849.  
  1850. /*
  1851.  * Memory allocation routines
  1852.  */
  1853.  
  1854.  
  1855. /*
  1856.  * Return pointer to block of size bytes, or NULL if there's
  1857.  * not enough memory available.
  1858.  *
  1859.  * This is called through RTFAlloc(), a define which coerces the
  1860.  * argument to int.  This avoids the persistent problem of allocation
  1861.  * failing and causing mysterious crashes under THINK C when a long is
  1862.  * passed.
  1863.  */
  1864.  
  1865. char *
  1866. _RTFAlloc (size)
  1867. int    size;
  1868. {
  1869.     return ((char *) malloc (size));
  1870. }
  1871.  
  1872.  
  1873. /*
  1874.  * Saves a string on the heap and returns a pointer to it.
  1875.  */
  1876.  
  1877.  
  1878. char *
  1879. RTFStrSave (s)
  1880. char    *s;
  1881. {
  1882. char    *p;
  1883.  
  1884.     if ((p = RTFAlloc ((int) (strlen (s) + 1))) == (char *) NULL)
  1885.         return ((char *) NULL);
  1886.     return (strcpy (p, s));
  1887. }
  1888.  
  1889.  
  1890. void
  1891. RTFFree (p)
  1892. char    *p;
  1893. {
  1894.     if (p != (char *) NULL)
  1895.         free (p);
  1896. }
  1897.  
  1898.  
  1899. /* ---------------------------------------------------------------------- */
  1900.  
  1901.  
  1902. /*
  1903.  * Token comparison routines
  1904.  */
  1905.  
  1906. int
  1907. RTFCheckCM (class, major)
  1908. int    class, major;
  1909. {
  1910.     return (rtfClass == class && rtfMajor == major);
  1911. }
  1912.  
  1913.  
  1914. int
  1915. RTFCheckCMM (class, major, minor)
  1916. int    class, major, minor;
  1917. {
  1918.     return (rtfClass == class && rtfMajor == major && rtfMinor == minor);
  1919. }
  1920.  
  1921.  
  1922. int
  1923. RTFCheckMM (major, minor)
  1924. int    major, minor;
  1925. {
  1926.     return (rtfMajor == major && rtfMinor == minor);
  1927. }
  1928.  
  1929.  
  1930. /* ---------------------------------------------------------------------- */
  1931.  
  1932.  
  1933. int
  1934. RTFCharToHex (c)
  1935. char    c;
  1936. {
  1937.     if (isupper (c))
  1938.         c = tolower (c);
  1939.     if (isdigit (c))
  1940.         return (c - '0');    /* '0'..'9' */
  1941.     return (c - 'a' + 10);        /* 'a'..'f' */
  1942. }
  1943.  
  1944.  
  1945. int
  1946. RTFHexToChar (i)
  1947. int    i;
  1948. {
  1949.     if (i < 10)
  1950.         return (i + '0');
  1951.     return (i - 10 + 'a');
  1952. }
  1953.  
  1954.  
  1955. /* ---------------------------------------------------------------------- */
  1956.  
  1957. /*
  1958.  * RTFReadOutputMap() -- Read output translation map
  1959.  */
  1960.  
  1961.  
  1962. /*
  1963.  * Read in a file describing the relation between the standard character set
  1964.  * and an RTF translator's corresponding output sequences.  Each line consists
  1965.  * of a standard character name and the output sequence for that character.
  1966.  *
  1967.  * outMap is an array of strings into which the sequences should be placed.
  1968.  * It should be declared like this in the calling program:
  1969.  *
  1970.  *    char *outMap[rtfSC_MaxChar];
  1971.  *
  1972.  * reinit should be non-zero if outMap should be initialized before reading the
  1973.  * file, zero otherwise.  (This allows the map to be constructed by reading
  1974.  * several files.)  It's assumed that any existing strings in the map were
  1975.  * allocated by RTFStrSave().  The map is initialized BEFORE any attempt is
  1976.  * made to read the file.
  1977.  *
  1978.  * If the filename is an absolute pathname, look in the specified location
  1979.  * only.  Otherwise try to find the file in the current directory or the
  1980.  * library directory.
  1981.  */
  1982.  
  1983. int
  1984. RTFReadOutputMap (file, outMap, reinit)
  1985. char    *file;
  1986. char    *outMap[];
  1987. int    reinit;
  1988. {
  1989. FILE    *f;
  1990. char    buf[rtfBufSiz];
  1991. char    *name, *seq;
  1992. int    stdCode;
  1993. int    i;
  1994. TSScanner    scanner;
  1995. char        *scanEscape;
  1996. char        *fn = "RTFReadOutputMap";
  1997.  
  1998.     /* clobber current mapping */
  1999.  
  2000.     if (reinit)
  2001.     {
  2002.         for (i = 0; i < rtfSC_MaxChar; i++)
  2003.         {
  2004.             RTFFree (outMap[i]);
  2005.             outMap[i] = (char *) NULL;
  2006.         }
  2007.     }
  2008.  
  2009.     if ((f = RTFOpenLibFile (file, "r")) == (FILE *) NULL)
  2010.         return (0);
  2011.  
  2012.     /*
  2013.      * Turn off scanner's backslash escape mechanism while reading
  2014.      * file.  Restore it later.
  2015.      */
  2016.     TSGetScanner (&scanner);
  2017.     scanEscape = scanner.scanEscape;
  2018.     scanner.scanEscape = "";
  2019.     TSSetScanner (&scanner);
  2020.  
  2021.     /* read file */
  2022.  
  2023.     while (fgets (buf, (int) sizeof (buf), f) != (char *) NULL)
  2024.     {
  2025.         if(buf[0] == '#')    /* skip comment lines */
  2026.             continue;
  2027.         TSScanInit (buf);
  2028.         if ((name = TSScan ()) == (char *) NULL)
  2029.             continue;    /* skip blank lines */
  2030.         if ((stdCode = RTFStdCharCode (name)) < 0)
  2031.         {
  2032.             RTFMsg ("%s: unknown character name: %s\n", fn, name);
  2033.             continue;
  2034.         }
  2035.         if ((seq = TSScan ()) == (char *) NULL)
  2036.         {
  2037.             RTFMsg ("%s: malformed output sequence line for character %s\n", fn, name);
  2038.             continue;
  2039.         }
  2040.         if ((seq = RTFStrSave (seq)) == (char *) NULL)
  2041.             RTFPanic ("%s: out of memory", fn);
  2042.         outMap[stdCode] = seq;
  2043.     }
  2044.     scanner.scanEscape = scanEscape;
  2045.     TSSetScanner (&scanner);
  2046.     fclose(f);
  2047.     return (1);
  2048. }
  2049.  
  2050.  
  2051. /* ---------------------------------------------------------------------- */
  2052.  
  2053. /*
  2054.  * Open a library file.
  2055.  */
  2056.  
  2057.  
  2058. static FILE    *(*libFileOpen) () = NULL;
  2059.  
  2060.  
  2061.  
  2062. void
  2063. RTFSetOpenLibFileProc (proc)
  2064. FILE    *(*proc) ();
  2065. {
  2066.     libFileOpen = proc;
  2067. }
  2068.  
  2069.  
  2070. FILE *
  2071. RTFOpenLibFile (file, mode)
  2072. char    *file;
  2073. char    *mode;
  2074. {
  2075.     if (libFileOpen == NULL)
  2076.         return ((FILE *) NULL);
  2077.     return ((*libFileOpen) (file, mode));
  2078. }
  2079.  
  2080.  
  2081. /* ---------------------------------------------------------------------- */
  2082.  
  2083. /*
  2084.  * Print message.  Default is to send message to stderr
  2085.  * but this may be overridden with RTFSetMsgProc().
  2086.  *
  2087.  * Message should include linefeeds as necessary.  If the default
  2088.  * function is overridden, the overriding function may want to
  2089.  * map linefeeds to another line ending character or sequence if
  2090.  * the host system doesn't use linefeeds.
  2091.  */
  2092.  
  2093.  
  2094. static void
  2095. DefaultMsgProc (s)
  2096. char    *s;
  2097. {
  2098.     fprintf (stderr, "%s", s);
  2099. }
  2100.  
  2101.  
  2102. static RTFFuncPtr    msgProc = DefaultMsgProc;
  2103.  
  2104.  
  2105. void
  2106. RTFSetMsgProc (proc)
  2107. RTFFuncPtr    proc;
  2108. {
  2109.     msgProc = proc;
  2110. }
  2111.  
  2112.  
  2113. # ifdef STDARG
  2114.  
  2115. /*
  2116.  * This version is for systems with stdarg
  2117.  */
  2118.  
  2119. void
  2120. RTFMsg (char *fmt, ...)
  2121. {
  2122. char    buf[rtfBufSiz];
  2123.  
  2124.     va_list args;
  2125.     va_start (args,fmt);
  2126.     vsprintf (buf, fmt, args);
  2127.     va_end (args);
  2128.     (*msgProc) (buf);
  2129. }
  2130.  
  2131. # else /* !STDARG */
  2132.  
  2133. # ifdef    VARARGS
  2134.  
  2135.  
  2136. /*
  2137.  * This version is for systems that have varargs.
  2138.  */
  2139.  
  2140. void
  2141. RTFMsg (va_alist)
  2142. va_dcl
  2143. {
  2144. va_list    args;
  2145. char    *fmt;
  2146. char    buf[rtfBufSiz];
  2147.  
  2148.     va_start (args);
  2149.     fmt = va_arg (args, char *);
  2150.     vsprintf (buf, fmt, args);
  2151.     va_end (args);
  2152.     (*msgProc) (buf);
  2153. }
  2154.  
  2155. # else    /* !VARARGS */
  2156.  
  2157. /*
  2158.  * This version is for systems that don't have varargs.
  2159.  */
  2160.  
  2161. void
  2162. RTFMsg (fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  2163. char    *fmt;
  2164. char    *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
  2165. {
  2166. char    buf[rtfBufSiz];
  2167.  
  2168.     sprintf (buf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  2169.     (*msgProc) (buf);
  2170. }
  2171.  
  2172. # endif    /* !VARARGS */
  2173. # endif /* !STDARG */
  2174.  
  2175.  
  2176. /* ---------------------------------------------------------------------- */
  2177.  
  2178.  
  2179. /*
  2180.  * Process termination.  Print error message and exit.  Also prints
  2181.  * current token, and current input line number and position within
  2182.  * line if any input has been read from the current file.  (No input
  2183.  * has been read if prevChar is EOF).
  2184.  */
  2185.  
  2186. static void
  2187. DefaultPanicProc (s)
  2188. char    *s;
  2189. {
  2190.     fprintf (stderr, "%s", s);
  2191.     exit (1);
  2192. }
  2193.  
  2194.  
  2195. static RTFFuncPtr    panicProc = DefaultPanicProc;
  2196.  
  2197.  
  2198. void
  2199. RTFSetPanicProc (proc)
  2200. RTFFuncPtr    proc;
  2201. {
  2202.     panicProc = proc;
  2203. }
  2204.  
  2205.  
  2206. # ifdef STDARG
  2207.  
  2208. /*
  2209.  * This version is for systems with stdarg
  2210.  */
  2211.  
  2212. void
  2213. RTFPanic (char *fmt, ...)
  2214. {
  2215. char    buf[rtfBufSiz];
  2216.  
  2217.     va_list args;
  2218.     va_start (args,fmt);
  2219.     vsprintf (buf, fmt, args);
  2220.     va_end (args);
  2221.     (void) strcat (buf, "\n");
  2222.     if (prevChar != EOF && rtfTextBuf != (char *) NULL)
  2223.     {
  2224.         sprintf (buf + strlen (buf),
  2225.             "Last token read was \"%s\" near line %ld, position %d.\n",
  2226.             rtfTextBuf, rtfLineNum, rtfLinePos);
  2227.     }
  2228.     (*panicProc) (buf);
  2229. }
  2230.  
  2231. # else /* !STDARG */
  2232.  
  2233. # ifdef    VARARGS
  2234.  
  2235.  
  2236. /*
  2237.  * This version is for systems that have varargs.
  2238.  */
  2239.  
  2240. void
  2241. RTFPanic (va_alist)
  2242. va_dcl
  2243. {
  2244. va_list    args;
  2245. char    *fmt;
  2246. char    buf[rtfBufSiz];
  2247.  
  2248.     va_start (args);
  2249.     fmt = va_arg (args, char *);
  2250.     vsprintf (buf, fmt, args);
  2251.     va_end (args);
  2252.     (void) strcat (buf, "\n");
  2253.     if (prevChar != EOF && rtfTextBuf != (char *) NULL)
  2254.     {
  2255.         sprintf (buf + strlen (buf),
  2256.             "Last token read was \"%s\" near line %ld, position %d.\n",
  2257.             rtfTextBuf, rtfLineNum, rtfLinePos);
  2258.     }
  2259.     (*panicProc) (buf);
  2260. }
  2261.  
  2262. # else    /* !VARARGS */
  2263.  
  2264. /*
  2265.  * This version is for systems that don't have varargs.
  2266.  */
  2267.  
  2268. void
  2269. RTFPanic (fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  2270. char    *fmt;
  2271. char    *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
  2272. {
  2273. char    buf[rtfBufSiz];
  2274.  
  2275.     sprintf (buf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  2276.     (void) strcat (buf, "\n");
  2277.     if (prevChar != EOF && rtfTextBuf != (char *) NULL)
  2278.     {
  2279.         sprintf (buf + strlen (buf),
  2280.             "Last token read was \"%s\" near line %ld, position %d.\n",
  2281.             rtfTextBuf, rtfLineNum, rtfLinePos);
  2282.     }
  2283.     (*panicProc) (buf);
  2284. }
  2285.  
  2286. # endif    /* !VARARGS */
  2287. # endif /* !STDARG */
  2288.